/**
 * Copyright 2018 asiainfo Inc.
 **/
package com.myz.service.hystrix.service.impl;


import com.myz.service.hystrix.service.IHelloServcie;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;

/**
 * https://www.cnblogs.com/wanggangblog/p/8550218.html
 * https://juejin.im/post/5d8c1649e51d45780803307f
 * https://juejin.im/post/5dccd0926fb9a02b4d2f2f24
 * https://juejin.im/post/5dd494dae51d453fbc0bd73b
 *
 * @author maoyz on 2018/9/7
 * @version: v1.0
 */
@Service
public class HelloServiceImpl implements IHelloServcie {

    private static final Logger LOGGER = LoggerFactory.getLogger(HelloServiceImpl.class);

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 使用断路器@HystrixCommand, 开启服务降级处理，指定fallback
     * threadPoolProperties　-> HystrixThreadPoolProperties(默认配置项)
     * commandProperties -> HystrixCommandProperties(默认配置项)
     *
     * @return
     */
    @HystrixCommand(fallbackMethod = "isError",
            groupKey = "hystrix",
            threadPoolKey = "IHelloServcie",
            ignoreExceptions = {NullPointerException.class},
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "6000")
            },
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "20"),
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "10"),
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "20000"),
                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "20")
            }
    )
    @Override
    public String index() {
        // hystrix-IHelloServcie-n
        LOGGER.info("******** index ThreadName = {} **********", Thread.currentThread().getName());
        // 最简请求获取String
        // String object = restTemplate.getForObject("http://SERVICE-MEMBER/member/index", String.class);

        ParameterizedTypeReference<String> type = new ParameterizedTypeReference<String>() {
        };
        ResponseEntity<String> responseEntity = restTemplate.exchange("http://SERVICE-MEMBER/member/index", HttpMethod.GET, null, type);
        LOGGER.info("************** responseEntity = {} ***************", responseEntity);
        return responseEntity.getBody();
    }

    /**
     * 使用信号量隔离策略 execution.isolation.strategy = SEMAPHORE
     *
     * @return
     */
    @HystrixCommand(fallbackMethod = "isError",
            ignoreExceptions = {NullPointerException.class},
            groupKey = "hystrix",
            threadPoolKey = "IHelloServcie",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "6000"),
                    @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
                    @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "20"),
                    @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "20"),
            },
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "20"),
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "10"),
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "20000"),
                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "20")
            }
    )
    @Override
    public String indexSemaphore() {
        LOGGER.info("******** indexSemaphore ThreadName = {} **********", Thread.currentThread().getName());

        ParameterizedTypeReference<String> type = new ParameterizedTypeReference<String>() {
        };
        ResponseEntity<String> responseEntity = restTemplate.exchange("http://SERVICE-MEMBER/member/index", HttpMethod.GET, null, type);
        LOGGER.info("************** responseEntity = {} ***************", responseEntity);
        return responseEntity.getBody();
    }

    /**
     * 请求合并, Consumer的Controller调用的方法,该方法返回值必须为Future类型
     * <p>
     * collapserProperties -> HystrixCollapserProperties
     * batchMethod合并方法, 此方法必须加上@HystrixCommand
     * timerDelayInMilliseconds 请求时间间隔在 20ms 之内的请求会被合并为一个请求,默认为 10ms
     * maxRequestsInBatch 设置触发批处理执行之前，在批处理中允许的最大请求数。
     *
     * @return
     */
    @HystrixCollapser(batchMethod = "batchRequest",
            scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
            collapserProperties = {
                    @HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
                    @HystrixProperty(name = "maxRequestsInBatch", value = "200"),
            })
    @Override
    public Future<String> batch(Integer id) {
        LOGGER.info("********** batch id = {} *************", id);
        return null;
    }

    /**
     * 熔断请求
     * HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD => circuitBreaker.requestVolumeThreshold
     * 默认 20 个;10s 内请求数大于 20 个时就启动熔断器，当请求符合熔断条件时将触发 getFallback()
     * <p>
     * HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE => circuitBreaker.errorThresholdPercentage
     * 请求错误率大于 50%时就熔断，然后 for 循环发起请求，当请求符合熔断条件时将触发 getFallback()
     * <p>
     * HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS => circuitBreaker.sleepWindowInMilliseconds
     * 默认 5 秒; 熔断多少秒后去尝试请求
     */
    @HystrixCommand(fallbackMethod = "isError",
            commandProperties = {
                    @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
                    @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
                    @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000"),
            })
    @Override
    public String circuit(Integer id) {
        LOGGER.info("************ circuit, id = {} ***************", id);
        if (1 == id) {
            throw new RuntimeException();
        }
        String object = restTemplate.getForObject("http://SERVICE-MEMBER/member/index", String.class);
        return object;
    }

    /**
     * java.lang.IllegalStateException: batch method must be annotated with HystrixCommand annotation, 需要加上@HystrixCommand
     */
    @HystrixCommand
    private List<String> batchRequest(List<Integer> ids) {
        LOGGER.info("************ 请求合并请求batchRequest, ids = {} ***************", ids);
        // 测试数据

        return Arrays.asList(new String[]{"a", "b", "c"});
    }


    /**
     * 指定的错误方法
     */
    public String isError() {
        LOGGER.info("******** isError ThreadName = {} **********", Thread.currentThread().getName());
        return "this is ribbon hystrix error page";
    }

    public String isError(Integer id) {
        return "this is ribbon hystrix error page";
    }
}
